﻿using System;
using System.Linq;
using System.Threading.Tasks;
using System.Transactions;
using Elsa.Services.Models;
using HengTong.Model;
using HengTong.Model.Db.SupplyChain.Purchase;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using TiaoTaoHelper.Wtm.Interfaces;
using TiaoTaoHelper.Wtm.Models;
using TiaoTaoHelper.Wtm.Workflow;
using UUIDNext;
using WalkingTec.Mvvm.Core;
using WalkingTec.Mvvm.Core.Extensions;
using WalkingTec.Mvvm.Core.WorkFlow;

namespace HengTong.ViewModel.SupplyChain.SysPurchaseRequestBillVms;

public partial class SysPurchaseRequestBillVm
{
    [WorkflowOperation("驳回", "将单据状态设置为草稿", "采购申请")]
    public void Reject(ActivityExecutionContext context)
    {
        Entity.BillStatus = BillStatus.草稿;
        if (Entity is IBasePoco poco)
        {
            poco.UpdateTime = DateTime.Now;
            DC.UpdateProperty(Entity, x => x.UpdateTime);
            if (context.Input is WtmApproveInput input)
            {
                poco.UpdateBy = input.CurrentUser?.ITCode ?? "workflow";
                DC.UpdateProperty(Entity, x => x.UpdateBy);
            }
        }

        DC.UpdateProperty(Entity, x => x.BillStatus);
        DC.SaveChanges();
    }

    [WorkflowOperation("取消", "将流程状态设置为取消", "采购申请")]
    public void Cancel(ActivityExecutionContext context)
    {
        if (Entity is IBasePoco poco)
        {
            poco.UpdateTime = DateTime.Now;
            DC.UpdateProperty(Entity, x => x.UpdateTime);
            if (context.Input is WtmApproveInput input)
            {
                poco.UpdateBy = input.CurrentUser?.ITCode ?? "workflow";
                DC.UpdateProperty(Entity, x => x.UpdateBy);
            }
        }

        if (Entity is IBill bill)
        {
            bill.WorkflowStatus = WorkflowStatus.已取消;
            DC.UpdateProperty(Entity, x => x.WorkflowStatus);
        }

        DC.SaveChanges();
    }

    [WorkflowOperation("完成", "将流程状态设置为已完成", "采购申请")]
    public async Task CompleteWithScheduling(ActivityExecutionContext context)
    {
        var scope = Wtm.ServiceProvider.CreateScope();
        var logger = scope.ServiceProvider.GetRequiredService<ILogger<SysPurchaseRequestBillVm>>();
        using var transactionScope = new TransactionScope(
            TransactionScopeOption.Required,
            new TransactionOptions
                { IsolationLevel = IsolationLevel.ReadCommitted, Timeout = TimeSpan.FromMinutes(10) },
            TransactionScopeAsyncFlowOption.Enabled);
        try
        {
            //更新单据状态
            if (Entity is IBasePoco poco)
            {
                poco.UpdateTime = DateTime.Now;
                DC.UpdateProperty(Entity, x => x.UpdateTime);
                if (context.Input is WtmApproveInput input)
                {
                    poco.UpdateBy = input.CurrentUser?.ITCode ?? "workflow";
                    DC.UpdateProperty(Entity, x => x.UpdateBy);
                }
            }

            if (Entity is IBill bill)
            {
                bill.WorkflowStatus = WorkflowStatus.已完成;
                DC.UpdateProperty(Entity, x => x.WorkflowStatus);
            }

            await DC.SaveChangesAsync();

            // 将采购申请单的明细加入到采购需求池中
            var entries = await DC.Set<SysPurchaseRequestBillEntry>()
                .AsNoTracking()
                .Include(e => e.Bill)
                .Include(e => e.Material)
                .ThenInclude(e => e.MaterialGroup)
                .Where(e => e.BillId == Entity.ID)
                .ToListAsync();
            var pools = await DC.Set<SysPurchaseRequirementPool>()
                .AsNoTracking()
                .Include(e => e.MaterialGroups)
                .Where(e => e.State == CommonState.启用)
                .ToListAsync();
            foreach (var entry in entries)
            {
                var pool = pools.FirstOrDefault(e =>
                               e.MaterialGroups.Any(g => g.GroupId == entry.Material.MaterialGroupId)) ??
                           pools.FirstOrDefault(e => e.IsDefault == true);
                if (pool == null) throw new Exception($"{entry.Material.Name}没有可用的采购需求池，请先配置采购需求池");
                var poolEntry = new SysPurchaseRequirementPoolEntry
                {
                    ID = Uuid.NewDatabaseFriendly(Database.SqlServer),
                    PoolId = pool.ID,
                    RequestBillId = entry.BillId,
                    RequestBillEntryId = entry.ID,
                    MaterialId = entry.MaterialId,
                    MaterialSkuId = entry.MaterialSkuId,
                    RequestQuantity = entry.RequestQuantity,
                    BudgetAmount = entry.BudgetAmount,
                    PurchasedQuantity = 0,
                    RemainingQuantity = entry.RequestQuantity,
                    GroupId = entry.Bill.GroupId,
                    RequestBy = entry.Bill.CreateBy,
                    PurchaseType = entry.Bill.PurchaseType,
                    MaterialType = entry.Bill.MaterialType,
                    UrgencyLevel = entry.Bill.UrgencyLevel,
                    EntryDate = DateTime.Now,
                    ApprovalDate = DateTime.Now,
                    RequiredDeliveryDate = entry.RequiredArrivalDate,
                    PurchaseState = PurchaseState.待立项,
                    CreateBy = "system",
                    CreateTime = DateTime.Now,
                    UpdateBy = "system",
                    UpdateTime = DateTime.Now
                };
                await DC.Set<SysPurchaseRequirementPoolEntry>().AddAsync(poolEntry);
                entry.PurchaseProgress = PurchaseProgress.采购需求池;
                await DC.Set<SysPurchaseRequestBillEntry>()
                    .Where(e => e.ID == entry.ID)
                    .ExecuteUpdateAsync(e => e.SetProperty(p => p.PurchaseProgress, entry.PurchaseProgress));
                await DC.SaveChangesAsync();
                transactionScope.Complete();
            }

            logger.LogInformation("采购申请单[{BillCode}]已完成，已将明细加入采购需求池", Entity.BillCode);
        }
        catch (Exception e)
        {
            logger.LogError(e, "处理采购申请单完成时发生异常，单据编码:{BillCode}", Entity.BillCode);
            throw;
        }
    }
    
    public override async Task DoEditAsync(bool updateAllFields = false)
    {
        if(Entity.NeedDate == null || Entity.NeedDate < DateTime.Now.AddDays(7))
        {
            MSD.AddModelError(" ", "需求日期不能为空且需至少提前7天.");
            return;
        }
            
        if (Entity is IBill { WorkflowStatus: WorkflowStatus.提交中 })
        {
            var raw = await DC.Set<SysPurchaseRequestBill>()
                .AsNoTracking()
                .FirstOrDefaultAsync(e => e.ID == Entity.ID);
            if (raw.WorkflowStatus != WorkflowStatus.待提交)
            {
                MSD.AddModelError(" ", "单据已提交审核,请勿重复提交.");
                return;
            }
            // 如果没有申请明细则拒绝
            var any = await DC.Set<SysPurchaseRequestBillEntry>()
                .Where(e => e.BillId == raw.ID)
                .AnyAsync();
            if (!any)
            {
                MSD.AddModelError(" ", "单据没有明细,无法提交审核.");
                return;
            }
            // 如果有明细的要求到货日期小于需求日期，则更新为需求日期
            await DC.Set<SysPurchaseRequestBillEntry>()
                .Where(e => e.BillId == Entity.ID)
                .Where(e => e.RequiredArrivalDate < Entity.NeedDate)
                .ExecuteUpdateAsync(e => 
                    e.SetProperty(p => p.RequiredArrivalDate, Entity.NeedDate)
                        .SetProperty(p => p.PurchaseProgress, PurchaseProgress.采购审批中));
                
            var result = await StartWorkflowAsync("采购申请流程");
            if (result == null) return;
            if (!result.Executed)
            {
                MSD.AddModelError(" ", $"提交审核异常. {result.Exception?.Message}");
                return;
            }
            Entity.BillStatus = BillStatus.正文;
            Entity.WorkflowStatus = WorkflowStatus.审核中;
        }

        if (Entity is IBasePoco poco)
        {
            poco.UpdateBy = Wtm.LoginUserInfo?.ITCode;
            poco.UpdateTime = DateTime.Now;
        }

        await base.DoEditAsync(updateAllFields);
    }
}